﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using IdeaNMR.Shared.Utils;
using IdeaNMR.Shared;

namespace IdeaNMR.Web.Core
{
    [Serializable]
    public class ComponentFassade : NMRObjectFassade, IEquatable<ComponentFassade>
    {
        private string _content;
        public string Content
        {
            get
            {
                if (this.ComponentType == ComponentType.Setup)
                {
                    string msg = "Content Property is not available for ComponentType Setup.";
                    throw new InvalidOperationException(msg);
                }
                return _content;
            }
            set
            {
                if (this.ComponentType == ComponentType.Setup)
                {
                    string msg = "Content Property is not available for ComponentType Setup.";
                    throw new InvalidOperationException(msg);
                }
                this._content = value;
            }
        }

        // Only when ComponentType is Script
        private string _type;
        public string Type
        {
            get
            {
                if (this.ComponentType != ComponentType.Script)
                {
                    string msg = string.Format("Type Property is not available for ComponentType {0}.", this.ComponentType.ToString());
                    throw new InvalidOperationException(msg);
                }
                return this._type;

            }
            set
            {
                if (this.ComponentType != ComponentType.Script)
                {
                    string msg = string.Format("Type Property is not available for ComponentType {0}.", this.ComponentType.ToString());
                    throw new InvalidOperationException(msg);
                }
                this._type = value;
            }
        }

        private bool _isIncludable;
        public bool IsIncludable
        {
            get
            {
                if (this.ComponentType == ComponentType.Script)
                    return this._isIncludable;
                else
                {
                    string msg = string.Format("IsIncludable Property is not available for ComponentType {0}.", this.ComponentType.ToString());
                    throw new InvalidOperationException(msg);
                }
            }
            set
            {
                if (this.ComponentType != ComponentType.Script)
                {
                    string msg = string.Format("IsIncludable Property is not available for ComponentType {0}.", this.ComponentType.ToString());
                    throw new InvalidOperationException(msg);
                }
                this._isIncludable = value;
            }
        }

        /// <summary>
        /// Initializes instance of ComponentFassade
        /// </summary>
        /// <param name="t">Which component will be represented by this instance.</param>
        /// <exception cref="System.NotSupportedException">When t is Setup.</exception>
        public ComponentFassade(ComponentType t)
            : base(t)
        {
            this._content = string.Empty;

            if (t == ComponentType.Setup)
                throw new NotSupportedException("ComponentFassade cannot be used for ComponentType.Setup. Use SetupFassade instead.");

            if (t == ComponentType.Script)
            {
                this.Type = string.Empty;
                this.IsIncludable = false;
            }
        }
        /// <summary>
        /// Initializes the Object with the values of the DB entity.
        /// </summary>
        /// <param name="id"></param>
        /// <exception cref="System.InvalidOperationException">When id is empty</exception>
        /// <exception cref="System.ArgumentException">When entity with given ID is not found.</exception>
        /// <exception cref="System.Unauthorizedaccessexception">When private entity does not belong to user.</exception>
        public override NMRObject Initialize(Guid id, string userName, IdeaNMRdbContainer inmr)
        {
            if (inmr == null) inmr = new IdeaNMRdbContainer();

            Component result = (Component)base.Initialize(id, userName, inmr);
            switch (this.ComponentType)
            {
                case ComponentType.Setup:
                    break;
                case ComponentType.Script:
                    Script s = (Script)result;
                    this.Type = s.Type;
                    this.IsIncludable = s.IsIncludable;
                    goto default;

                // Default is everything but Setup
                default:
                    // Everythig but Setup is Component
                    this._content = result.Content.ToLocalNewLine();
                    break;
            }
            return result;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="cof"></param>
        /// <exception cref="System.InvalidOperationException">When ComponentTypes do not equal.</exception>
        public void CopyTo(ComponentFassade cof)
        {
            if (cof.ComponentType != this.ComponentType)
            {
                throw new InvalidOperationException("The type of target Component does not match type of this object.");
            }
            base.CopyTo(cof);

            //Everithing but setup is default
            switch (this.ComponentType)
            {
                case ComponentType.Setup:
                    cof.Type = this.Type;
                    break;
                case ComponentType.Script:
                    cof.Type = this.Type;
                    cof.IsIncludable = this.IsIncludable;
                    goto default;
                default:
                    cof.Content = this.Content;
                    break;
            }
        }

        /// <summary>
        /// Saves current 
        /// </summary>
        /// <param name="userName"></param>
        /// <exception cref="System.InvalidOperationExeption">When cannot find somethin in Object Content like FirstVersion or Template.
        /// Or when found an entry that is not draft.</exception>
        /// <exception cref="System.UnauthorizedAccessException">When this.Id point to object that does not belong to username.</exception>
        /// <remarks>
        /// <para>
        /// Security. If <c>entity</c> is found by <c>T entity = base.GetEntityById<T>(this.Id, inmr);<T>(this.Id, inmr);</c>
        /// Verify that it is the correct one, so that you DONT overwrite somebody's else data.
        /// </para>
        /// </remarks>
        public override NMRObject SaveToDatabase(string userName, IdeaNMRdbContainer inmr)
        {
            if (inmr == null) inmr = new IdeaNMRdbContainer();
            switch (this.ComponentType)
            {
                case ComponentType.PulseProgram:
                    return this.SaveToDatabase<Pulseprogram>(userName, inmr);
                case ComponentType.WaveShape:
                    return this.SaveToDatabase<Wave>(userName, inmr);
                case ComponentType.CPD:
                    return this.SaveToDatabase<Cpd>(userName, inmr);
                case ComponentType.Script:
                    return this.SaveToDatabase<Script>(userName, inmr);
                case ComponentType.Setup:
                    throw new InvalidOperationException("ComponentFassade shouldn't have ComponentType.Setup");
                default:
                    string msg = string.Format("ComponentFassade is not implemented for ComponentType {0}.", this.ComponentType.ToString());
                    throw new NotImplementedException(msg);
            }
        }
        protected T SaveToDatabase<T>(string userName, IdeaNMRdbContainer inmr)
            where T : Component, new()
        {
            if (inmr == null) inmr = new IdeaNMRdbContainer();
            T entity = base.GetEntityById<T>(this.Id, inmr);

            // if entity is null - means it is first time 
            // the current object is saved to the db
            if (entity == null)  //Create new entry
            {
                entity = new T();
                entity.Id = this.Id;

                // Find a FirstVersionId in ObjectContent if it is present in this.FirstVersionId
                T fV = null;  // First Version is exists
                if (this.FirstVersionId != Guid.Empty)
                    fV = base.GetEntityById<T>(this.FirstVersionId, inmr);
                else fV = entity;
                if (fV == null) throw new InvalidOperationException("There is not FirstVersion with given Id.");
                entity.FirstVersion = fV;

                T tE = null;
                if (this.TemplateId != Guid.Empty)
                    tE = base.GetEntityById<T>(this.TemplateId, inmr);
                else tE = entity;
                if (tE == null) throw new InvalidOperationException("There is not Template with given Id.");
                entity.Template = tE;
            }
            else // Clear authors
            {
                /**************IMPORTANT**************/
                ValidateAuthorization(entity, userName);

                if (!entity.Authors.IsLoaded) entity.Authors.Load();
                NumberedAuthor[] nas = entity.Authors.ToArray<NumberedAuthor>();
                foreach (NumberedAuthor na in nas) inmr.DeleteObject(na);
            }
            FillEntity<T>(entity, inmr);

            inmr.SaveChanges();

            this.GetDevelopments(userName, entity);
            return entity;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="entity"></param>
        /// <param name="source"></param>
        /// <exception cref="System.NullReferenceException">When entity is not scipt and this.ComponentType is script.</exception>
        protected void FillEntity<T>(T entity, IdeaNMRdbContainer source)
            where T : Component
        {
            base.FillEntity(entity, source);

            entity.Content = this.Content;
            switch (this.ComponentType)
            {
                case ComponentType.Script:
                    Script scr = entity as Script;
                    scr.Type = this.Type;
                    scr.IsIncludable = this.IsIncludable;
                    break;
            }
        }


        #region IEquatable<ComponentFassade> Members
        public override int GetHashCode()
        {
            int result = base.GetHashCode();
            switch (this.ComponentType)
            {
                case ComponentType.Setup:
                    result ^= this.Type.GetHashCode();
                    break;
                case ComponentType.Script:
                    result ^= this.Type.GetHashCode();
                    result ^= this.IsIncludable.GetHashCode();
                    goto default;
                default:
                    result ^= this.Content.GetHashCode();
                    break;
            }

            return result;
        }

        public bool Equals(ComponentFassade other)
        {
            if (other == null) return false;
            return this.GetHashCode() == other.GetHashCode();
        }

        public override bool Equals(object other)
        {
            if (!(other is ComponentFassade))
                throw new InvalidCastException("The 'other' argument is not a ComponentFassade object.");
            else
                return Equals(other as ComponentFassade);
        }
        #endregion
    }
}
